package com.cd.university.service.abs;

import com.cd.university.common.RestResult;
import com.cd.university.common.UniversityConstants;
import com.cd.university.common.utils.PageUtil;
import com.cd.university.common.utils.SpringUtil;
import com.cd.university.db.mapper.ProjectExamineMapper;
import com.cd.university.db.mapper.ProjectFileMapper;
import com.cd.university.db.mapper.ProjectMapper;
import com.cd.university.db.mapper.UserMapper;
import com.cd.university.db.pojo.Message;
import com.cd.university.db.pojo.ProjectFile;
import com.cd.university.service.AchievementProcessor;
import com.cd.university.service.AssessmentService;
import com.cd.university.service.MessageService;
import com.cd.university.service.impl.ProjectExamineServiceImpl;
import com.cd.university.service.impl.ProjectFileServiceImpl;
import com.cd.university.service.impl.ProjectServiceImpl;
import com.cd.university.vo.*;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 抽象类，统一实现了结题申请里面成果形式的重复功能
 * @author 陈栋
 * @create 2021/11/3 16:51
 */
@Component
public abstract class AbstractAchievementProcessor<C,T> implements AchievementProcessor {

    @Autowired
    public ProjectFileServiceImpl projectFileService;

    @Autowired
    public ProjectExamineServiceImpl projectExamineService;

    @Autowired
    public ProjectServiceImpl projectService;

    @Autowired
    public ProjectMapper projectMapper;

    @Autowired
    public ProjectExamineMapper projectExamineMapper;

    @Autowired
    public ProjectFileMapper projectFileMapper;

    @Autowired
    public RedisTemplate redisTemplate;

    @Autowired
    public UserMapper userMapper;

    @Autowired
    private MessageService messageService;

    @Autowired
    private AssessmentService assessmentService;


    /**
     * 存储缓存名字的map
     * key为类名，value为存储库中的key
     */
    public static final HashMap<String, String> repositoryName = new HashMap<>();

    static {
        repositoryName.put("PaperProcessor","paperVos");
        repositoryName.put("PatentProcessor","patentVos");
        repositoryName.put("AwardedProcessor","awardedVos");
    }

    @Override
    public RestResult applyFile(MultipartFile file,
                                String primaryId,
                                Integer projectId,
                                Integer atId,
                                Integer categoryId) {
        ProjectExamineVo projectExamineVo = searchPEVoByProjectId(projectId);
        // 判断该项目的状态时否是3：也就是项目已经处于立项的状态
        if (!projectExamineVo.getStatusId().equals(UniversityConstants.PROJECT_STATUS_SECOND_PASS)){
            return null;
        }
        // 设置项目的状态为立项
        Integer status = UniversityConstants.PROJECT_FILE_NOT_PASS;
        ProjectFile projectFile = projectFileService.getProjectFile(file, projectId, primaryId, atId, categoryId, status);
        // 上传到项目文件表中
        projectFileMapper.insert(projectFile);
        return RestResult.success("上传文件成功")
                .put("data",projectFile);
    }

    /**
     * 这里面需要做的事情如下
     * 1：把基本数据插入到对应的表里面
     * 2：生成对应的Vo对象
     * 3：把Vo对象更新到Redis中
     * @param data 基本数据
     * @return
     */
    @Override
    public RestResult applyData(Object data) {
        // 向数据库中插入结题申请的数据
        C achievement = insertData(data);

        if (achievement==null) {
            return RestResult.failure("该项目不合法，不能进行结题申请");
        }

        // 更新本项目的状态全部为4
        T examineVo = generateVo(achievement);

        // 保存在缓存中
        save(examineVo);

        // 发送消息
        AbstractAchievementProcessor bean = SpringUtil.getBean(getClass());
        bean.saveMessage(examineVo);

        return RestResult.success("上传数据成功")
                .put("data",achievement);
    }

    @Async
    protected abstract void saveMessage(T examineVo);

    /**
     * 插入数据的抽象方法
     * @param data 基本数据
     * @return 具体的成果形式类
     */
    public abstract C insertData(Object data);

    /**
     * 抽象方法
     * @param data 基本成果数据
     * @return 生成一个审核成果Vo对象
     */
    @Async
    public abstract T generateVo(C data);

    /**
     * 生成Vo对象并且存入到Redis中
     * @param examineVo 成果数据Vo对象
     */
    @Async
    public void save(T examineVo) {
        setRedisTemplateValue(getThisClassName(),examineVo);
    }

    @Override
    public RestResult searchAllAchievement(Integer pageNum, Integer pageSize) {
        List<T> list = getValueFromRedis(getThisClassName());
        // 分页
        PageInfo pageInfo = projectService.pageList(pageNum, pageSize, list);
        return RestResult.success("审核的数据如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult searchAllPassAchievement(Integer pageNum, Integer pageSize) {
        List<T> list = getValueFromRedis(getThisClassName());
        List<T> results = passedResults(list);

        PageInfo pageInfo = PageUtil.startPage(pageNum,pageSize,results);
        return RestResult.success("审核的数据如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult searchAchievementByName(Integer pageNum, Integer pageSize, String name) {
        // 查询具体的Vo集合
        List<T> list = searchExamineByName(name);
        // 分页
        PageInfo pageInfo = projectService.pageList(pageNum, pageSize, list);
        // 返回
        return RestResult.success("查询数据如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult searchAchievementByUserName(Integer pageNum, Integer pageSize, String name) {
        // 调用具体类的方法拿到ExamineVo的集合
        List<T> result = searchByUserName(getValueFromRedis(getThisClassName()),name);

        PageInfo pageInfo = projectService.pageList(pageNum, pageSize, result);
        return RestResult.success(name + "所有的成果如下")
                .put("data",pageInfo);
    }

    /**
     * 获得所有的已经通过审核的成果
     * @param list 所有的成果
     * @return 已经通过的集合
     */
    public abstract List<T> passedResults(List<T> list);

    public abstract List<T> searchByUserName(List<T> list,String userName);

    /**
     * 当成果通过审批之后，根据项目的名称更新项目表以及项目审核表里面的状态
     * @param statusId 状态
     * @param projectName 项目名称
     */
    @Async
    public boolean updateDbStatus(Integer statusId, String projectName) {
        HashMap<String, Object> param = new HashMap<>();
        param.put("statusId",statusId);
        param.put("projectName",projectName);
        try {
            // 修改项目的状态
            projectMapper.changeProjectStatus(param);
            // 修改项目审核的状态
            projectExamineMapper.changePEStatusByName(param);

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 更新Project类以及ProjectExamine类中的Redis的内容
     * @param projectId 项目id
     * @param statusId 状态
     * @param fileId 成果形式的主键文件
     * @return 是否更新成功
     */
    @Async
    public void updateRedisStatus(Integer projectId, Integer statusId, String fileId) {
        try {
            // 更新Redis中的项目以及项目文件
            projectService.updateStatusFromRedis(projectId, statusId, null,null);
            // 更新Redis中的项目审核
            projectExamineService.updateProjectExamine();

            // 更新项目考核的Redis
            assessmentService.loadToRedis();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Async
    public void approveSaveMessage(Integer projectId, String projectName, Integer reviewerId, Integer atId, Integer categoryId) {
        Message message = new Message(projectId, projectName, reviewerId, atId, categoryId, UniversityConstants.MESSAGE_STATUS_APPLICATION_PASS);
        messageService.submitFinalApplication(message);
    }

    /**
     * 更新数据库中的项目文件的状态
     * @param projectId 项目id
     * @param statusId 状态
     * @param fileId 成果形式的主键文件
     * @return 是否更新成功
     */
    public boolean updateDbProjectFileStatus(Integer projectId, Integer statusId, String fileId) {
        return projectFileService.updateDbProjectFileStatus(projectId, statusId, fileId);
    }

    /**
     * 通过项目id搜索通过初审的项目的所有信息
     * @param projectId 项目id
     * @return 项目审核信息
     */
    public ProjectExamineVo searchPEVoByProjectId(Integer projectId) {

        ProjectExamineVo projectExamineVo = projectExamineService.searchExamineVoById(projectId);
        if (projectExamineVo==null) {
            throw new RuntimeException("该项目不存在");
        }
        return projectExamineVo;
    }

    /**
     * 拿到具体实现类的类名，通过类名从map中拿到value
     * @return map中的value
     */
    public String getThisClassName() {
        String className = getClass().getSimpleName();
        return className;
    }

    private void setRedisTemplateValue(String key, T examineVo) {
        List<T> examineVoList = getValueFromRedis(key);
        if (examineVoList==null) {
            examineVoList = new ArrayList<>();
        }
        examineVoList.add(examineVo);
        // 重新存入Redis中
        redisTemplate.opsForValue().getAndSet(repositoryName.get(key), examineVoList);
    }

    /**
     * 通过key从redis中拿到value
     * @param key 本类的类名
     * @return redis中的value
     */
    public List<T> getValueFromRedis(String key) {
        ValueOperations opsForValue = redisTemplate.opsForValue();
        Object o = opsForValue.get(repositoryName.get(key));

        // 如果redis中为空那么再从数据库中加载全部
        if (o==null) {
            loadToRedis();
            o = opsForValue.get(repositoryName.get(key));
        }
        List<T> examineVoList = (List<T>) o;
        return examineVoList;
    }

    /**
     * 把数据库中的数据加载到redis中
     */
    public abstract void loadToRedis();

    /**
     * 根据成果的名称搜索成果集合
     * @param name 成名称
     * @return 成果集合
     */
    public abstract List<T> searchExamineByName(String name);
}
